package message

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"gitee.com/zhongguo168a/go-nodex/nodex/enums/Serialize"
	"gitee.com/zhongguo168a/gocodes/datax/binaryx"
	"gitee.com/zhongguo168a/gocodes/datax/coderx"
	"gitee.com/zhongguo168a/gocodes/datax/jsonmap"
	"gitee.com/zhongguo168a/gocodes/datax/reflectx"
	"gitee.com/zhongguo168a/gocodes/myx/errorx"
	"io"
)

func Unpack(data []byte, serializeMode Serialize.Mode, args interface{}) error {
	switch serializeMode {
	case Serialize.JSON: // json
		m := map[string]interface{}{}
		err := json.Unmarshal(data, &m)
		if err != nil {
			err = errorx.Wrap(err, "unmarshal json")
			return err
		}
		err = jsonmap.MapToStruct(m, args)
		if err != nil {
			err = errorx.Wrap(err, "map to struct")
			return err
		}
	case Serialize.BYTE: // byte
		object, ok := args.(reflectx.IRefObject)
		if ok == false {
			return errorx.New("not IRefObject")
		}
		coder, err := coderx.NewByteToMapWithType(object.RefType())
		if err != nil {
			return errorx.Wrap(err, "NewByteToMapWithType")
		}
		m := map[string]interface{}{}

		var reader = bytes.NewReader(data)
		err = coder.Read(reader, m)
		if err != nil {
			return errorx.Wrap(err, fmt.Sprintf("ByteToMap"))
		}
		err = coderx.NewMapToRef().SetSource(m).Write(object)
		if err != nil {
			return errorx.Wrap(err, fmt.Sprintf("MapToRef"))
		}
	}
	return nil
}

func Pack(writer io.Writer, serializeMode Serialize.Mode, args interface{}) error {
	switch serializeMode {
	case Serialize.JSON: // json
		var m = map[string]interface{}{}
		switch args.(type) {
		case map[string]interface{}:
		default:
			err := jsonmap.StructToMap(args, m)
			if err != nil {
				err = errorx.Wrap(err, "map to struct")
				return err
			}
		}
		b, err := json.Marshal(m)
		if err != nil {
			return errorx.Wrap(err, "json.Marshal")
		}

		err = binaryx.WriteBytesLength(writer, binary.LittleEndian, b)
		if err != nil {
			err = errorx.Wrap(err, fmt.Sprintf("WriteBytesLength"))
			return err
		}
	case Serialize.BYTE: // byte
		object, ok := args.(reflectx.IRefObject)
		if ok == false {
			return errorx.New("not IRefObject")
		}
		m := map[string]interface{}{}
		if err := coderx.NewRefToMap().SetSource(object).Write(m); err != nil {
			return errorx.Wrap(err, "RefToMa")
		}

		coder, err := coderx.NewMapToByteWithType(object.RefType())
		if err != nil {
			return errorx.Wrap(err, "NewMapToByteWithType")
		}
		if err = coder.Write(m, writer); err != nil {
			return errorx.Wrap(err, fmt.Sprintf("MapToByte"))
		}
	}
	return nil
}
